package com.sxhuayuan.parking.plugin.parking.dahua;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import io.netty.util.internal.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sxhuayuan.parking.compenent.Message;
import com.sxhuayuan.parking.compenent.web.RespMessage;
import com.sxhuayuan.parking.config.CarActType;
import com.sxhuayuan.parking.config.Platform;
import com.sxhuayuan.parking.dto.CreateMcardChargeOrderDTO;
import com.sxhuayuan.parking.dto.ParkFeeCacheBean;
import com.sxhuayuan.parking.entity.Member;
import com.sxhuayuan.parking.entity.MemberCarPlate;
import com.sxhuayuan.parking.entity.Order;
import com.sxhuayuan.parking.entity.Order.Method;
import com.sxhuayuan.parking.entity.ParkChannel;
import com.sxhuayuan.parking.entity.ParkingIn;
import com.sxhuayuan.parking.entity.ParkingInOut;
import com.sxhuayuan.parking.entity.ParkingLot;
import com.sxhuayuan.parking.exception.MyException;
import com.sxhuayuan.parking.plugin.parking.ParkingPlugin;
import com.sxhuayuan.parking.plugin.parking.dahua.request.DaHuaBeforehandFeeRequest;
import com.sxhuayuan.parking.plugin.parking.dahua.request.SluiceControlRequest;
import com.sxhuayuan.parking.plugin.parking.dahua.result.DaHuaResult;
import com.sxhuayuan.parking.plugin.parking.result.McardCancelResultWrap;
import com.sxhuayuan.parking.plugin.parking.result.McardChargeOrderCreateResultWrap;
import com.sxhuayuan.parking.plugin.parking.result.McardQueryChargeOptionsResultWrap;
import com.sxhuayuan.parking.plugin.parking.result.McardQueryResultWrap;
import com.sxhuayuan.parking.plugin.parking.result.ParkingLotInfoQueryResultWrap;
import com.sxhuayuan.parking.plugin.parking.result.QueryParkingFeeResultWrap;
import com.sxhuayuan.parking.queue.msg.NotifyParkingLotMsg;
import com.sxhuayuan.parking.service.ExitingCarService;
import com.sxhuayuan.parking.service.OrderService;
import com.sxhuayuan.parking.service.ParkChannelService;
import com.sxhuayuan.parking.service.ParkFeeCacheService;
import com.sxhuayuan.parking.service.ParkingInOutService;
import com.sxhuayuan.parking.service.ParkingInService;
import com.sxhuayuan.parking.service.ParkingLotService;
import com.sxhuayuan.parking.service.RedisService;
import com.sxhuayuan.parking.service.impl.pojo.ExitingCar;
import com.sxhuayuan.parking.utils.DateUtils;
import com.sxhuayuan.parking.utils.SettingUtils;

@Component(DaHuaParkingPlugin.ID)
public class DaHuaParkingPlugin extends ParkingPlugin {

	public static final String ID = "daHuaParkingPlugin";
	private Logger log = LoggerFactory.getLogger(getClass());

	private static final Map<String, String> HEADER = new HashMap<>();

	static {
		HEADER.put("Content-Type", "application/json;charset=UTF-8");
	}

	@Autowired
	private ParkingLotService lotService;
	@Autowired
	private ParkingInService inService;
	@Autowired
	private ParkChannelService channelService;
	@Autowired
	private OrderService orderService;
	@Autowired
	private ExitingCarService exitingCarService;
	@Autowired
	private ParkingInOutService outService;
	@Autowired
	private RedisService redisService;
	@Autowired
	private ParkFeeCacheService parkFeeCacheService;

	private static final String SUCCESS = "success";
	private static final String ERRMSG = "errMsg";

	@Override
	public String getId() {
		return ID;
	}

	@Override
	public String getName() {
		return "大华停车";
	}

	@Override
	public boolean isSupportOrderNotify() {
		return true;
	}

	@Override
	public boolean isSupportEventPush() {
		return true;
	}

	@Override
	public boolean isOpenNotifyWhileDebug() {
		return true;
	}

	@Override
	public ParkFeeCacheBean saveParkInRecord(String lotSn, String carPlate, String resp) {
		return JSONObject.parseObject(resp, ParkFeeCacheBean.class);
	}

	@Override
	public QueryParkingFeeResultWrap queryParkingFee(ParkingLot lot, String carPlate) {
		log.debug("大华--请求算费--开始 carPlate = {}, lotName = {}", carPlate, lot.getName());
		DaHuaBeforehandFeeRequest req = new DaHuaBeforehandFeeRequest(carPlate, 1);
		String resp = httpPost(lot, "/ipms/integration/kingdo/payment/info", JSONObject.toJSONString(req));
		log.debug("大华--请求算费--开始 carPlate = {}, json = {}", carPlate, resp);
		JSONObject result = JSONObject.parseObject(resp);
		QueryParkingFeeResultWrap rs = new QueryParkingFeeResultWrap();
		boolean isSuccess = result.getBooleanValue(SUCCESS);
		rs.setIsSuccess(isSuccess);
		if (!isSuccess) {
			rs.setMsg(result.getString(ERRMSG));
			return rs;
		}
		ParkingIn in = inService.findByCarPlate(lot.getId(), carPlate);
		boolean existData = result.containsKey("data");
		// 如果入场记录不存在 但是算费存在记录并且费用不为零
		if (in == null) {
			if (existData) {
				// 如果有对应入场时间的出场, 则不算费
				JSONObject datas = result.getJSONObject("data");
				Date dataInTime = new Date(datas.getLong("inParkTime"));
				ParkingInOut verifyOut = outService.findByInTimeAndParkingLotIdAndCarPlate(dataInTime, lot.getId(), carPlate);
				if(verifyOut != null){
					throw new MyException("未找到入场纪录");
				}
				in = inService.enter(lot, carPlate, dataInTime, null, lot.getFreeMinutes(), null);
			} else {
				throw new MyException("未找到入场纪录");
			}
		}
		rs.setCarPlate(carPlate);
		String inTime = DateUtils.format(in.getInTime());
		rs.setEntertime(inTime);
		rs.setCalcStartTime(inTime);
		rs.setExtraMchid(lot.getSn());
		rs.setExtraPluginId(ID);
		// 如果data 不存在 算费截至时间取当前时间
		if (!existData) {
			Date endTime = new Date();
			rs.setCalcEndTime(DateUtils.format(endTime));
			long parkTime = endTime.getTime() - in.getInTime().getTime();
			rs.setParktimeMinute((int) Math.ceil(parkTime / 60000f));
			rs.setCalcFee(0f);
			rs.setFee(0f);
			rs.setUniqueId("");
			return rs;
		}
		JSONObject data = result.getJSONObject("data");
		float fee = data.getFloat("receivableMoney");
		// 缺失字段 cardNo no lotOrderNo orderNo rules
		int parkSecond = data.getIntValue("parkTime");
		String endTime = DateUtils.format(new Date(DateUtils.parse(inTime).getTime() + parkSecond * 1000));
		rs.setCalcEndTime(endTime);
		rs.setParktimeMinute((int) Math.ceil(parkSecond / 60f));
		rs.setCalcFee(fee);
		rs.setFee(fee);
		String newResp = fee == 0 ? "" : saveQueryParkingFee(lot, resp);
		Long uniqueId = submitUpdateOriginalInfoTask(lot.getSn(), carPlate, fee, newResp);
		rs.setUniqueId(uniqueId == null ? "" : String.valueOf(uniqueId));
		return rs;
	}

	/**
	 *
	 * @param lot
	 *            车厂
	 * @param resp
	 *            算费返回的json
	 * @return ParkFeeCacheBean 对象的json字符串
	 */
	private String saveQueryParkingFee(ParkingLot lot, String resp) {
		JSONObject result = JSONObject.parseObject(resp);
		JSONObject data = result.getJSONObject("data");
		ParkFeeCacheBean bean = new ParkFeeCacheBean();
		bean.setParkPluginId(ID);
		String carPlate = data.getString("carNum");
		ParkingIn in = inService.findByCarPlate(lot.getId(), carPlate);
		bean.setUniqueId(in.getUniqueId());
		bean.setCarPlate(carPlate);
		Date endTime = new Date(data.getLong("inParkTime") + data.getIntValue("parkTime") * 1000);
		bean.setEndTime(endTime);
		Float fee = data.getFloat("receivableMoney");
		bean.setTotalFee(fee);
		bean.setDiscountFee(0f);
		return JSONObject.toJSONString(bean);
	}

	@Override
	public QueryParkingFeeResultWrap queryLeavingCarParkingLotOrder(ParkingLot lot, String exitSn, Member member) {
		ParkChannel channel = channelService.findBySn(exitSn);
		log.debug("大华--出口算费--开始 lotName = {}, exitName = {}", lot.getSn(), channel.getName());
		ExitingCar exitingCar = exitingCarService.get(exitSn);
		QueryParkingFeeResultWrap rs = new QueryParkingFeeResultWrap();
		if (exitingCar == null) {
			rs.setIsSuccess(false);
			rs.setMsg("未找到费用");
			return rs;
		}
		rs.setIsSuccess(true);
		// 缺失字段 cardNo no lotOrderNo orderNo rules
		String plate = exitingCar.getCarPlate();
		String inTime = exitingCar.getInTime();
		String outTime = exitingCar.getOutTime();
		Float fee = exitingCar.getFee();
		rs.setCarPlate(plate);
		rs.setEntertime(inTime);
		rs.setCalcStartTime(inTime);
		rs.setCalcEndTime(outTime);
		rs.setParktimeMinute(exitingCar.getParkMinutes());
		rs.setCalcFee(fee);
		rs.setFee(fee);
		rs.setExtraMchid(lot.getSn());
		rs.setExtraPluginId(ID);
		ParkFeeCacheBean bean = new ParkFeeCacheBean();
		bean.setParkPluginId(ID);
		ParkingIn in = inService.findByCarPlate(lot.getId(), plate);
		if (in == null) {
			in = inService.enter(lot, plate, DateUtils.parse(exitingCar.getInTime()), null, lot.getFreeMinutes(), null);
		}
		bean.setUniqueId(in.getUniqueId());
		bean.setCarPlate(plate);
		bean.setEndTime(DateUtils.parse(outTime));
		bean.setTotalFee(fee);
		bean.setDiscountFee(0f);
		bean.setExitId(channel.getId());
		Long uniqueId = submitUpdateOriginalInfoTask(lot.getSn(), plate, fee, JSONObject.toJSONString(bean));
		rs.setUniqueId(uniqueId == null ? "" : String.valueOf(uniqueId));
		log.debug("大华--出口算费--成功 carPlate = {}, money = {}, lotName = {} , channelname = {}", plate, fee, lot.getName(), channel.getName());
		return rs;
	}

	@Override
	public boolean notifyParkingFeeOrderSuccess(NotifyParkingLotMsg msg) {
		log.debug("大华--支付下发--开始 json = {}", JSONObject.toJSONString(msg));
		Order order = orderService.findBySn(msg.getLocalOrderSn());
		ParkingLot lot = lotService.findBySn(msg.getLocalLotSn());
		String carPlate = msg.getCarPlate();
		Boolean notifyResult = false;
		boolean isUp = false;
		String redisKey = null;
		try {
			ExitingCar exitingCar = null;
			Method method = order.getMethod();
			// 出口支付 直接抬杆
			if (method.equals(Method.exit)) {
				if (!StringUtils.isEmpty(msg.getLocalChannelSn())) {
					exitingCar = exitingCarService.get(msg.getLocalChannelSn());
					if (exitingCar != null && exitingCar.getCarPlate().equals(carPlate)) {
						isUp = true;
						redisKey = msg.getLocalChannelSn();
					} else {
						return false;
					}
				} else {
					return false;
				}
			} else if (method.equals(Method.prepay)) {
				List<ParkChannel> channels = channelService.findByParkingLotIdAndType(lot.getId(), ParkChannel.Type.exit);
				if (channels != null && channels.size() > 0) {
					// 轮询出口缓存是否有相同车牌
					for (ParkChannel channelObj : channels) {
						exitingCar = exitingCarService.get(channelObj.getSn());
						if (exitingCar != null && exitingCar.getCarPlate().equals(carPlate)) {
							isUp = true;
							redisKey = channelObj.getSn();
							break;
						}
					}
				}
			} else {
				return false;
			}
			// 抬杆动作
			if (isUp) {
				String resp = null;
				for(int i = 0 ; i < 3 ; i++){
					resp = httpPost(lot, "/ipms/subSystem/control/sluice", JSON.toJSONString(SluiceControlRequest.open(exitingCar.getDevice())));
					Thread.sleep(100);
				}
				JSONObject upResult = JSONObject.parseObject(resp);
				notifyResult = upResult.getBoolean(SUCCESS);
				if (notifyResult) {
					log.debug("大华--手动抬杆--开始 carPlate = {}, lotName = {}", carPlate, lot.getName());
					// 抬杆成功之后进行出场
					ParkingIn in = inService.findByCarPlate(lot.getId(), carPlate);
					// 如果存在多比订单
					List<Order> orders = orderService.findByUniqueId(in.getUniqueId());
					BigDecimal money = order.getMoney();
					if (orders != null && orders.size() > 1) {
						money = new BigDecimal(0);
						for (Order o : orders) {
							money = money.add(o.getMoney());
						}
					}
					// 组装出场类
					ParkingInOut out = new ParkingInOut();
					out.setOutTime(DateUtils.parse(exitingCar.getOutTime()));
					out.setLotRecordId(exitingCar.getLotRecordId());
					out.setExitId(exitingCar.getExitid());
					out.setMoney(money);
					out.setPlatform(order.getPlatform());
					out.setCarActType(CarActType.temporary);
					outService.out(in, out);
					// 清除redis中缓存
					exitingCarService.remove(redisKey);
					log.debug("大华--手动抬杆--结束 carPlate = {}, lotName = {}", carPlate, lot.getName());
				}
				return notifyResult;
			}

			// 预支付
			log.debug("大华--预支付结果下发 carPlate = {}", carPlate);
			String fee = order.getMoney().toString();
			DaHuaBeforehandFeeRequest request = new DaHuaBeforehandFeeRequest(msg.getCarPlate(), fee, fee);
			String resp = httpPost(lot, "/ipms/integration/kingdo/payment/success", JSONObject.toJSONString(request));
			JSONObject result = JSONObject.parseObject(resp);
			notifyResult = result.getBooleanValue(SUCCESS);
			log.debug("大华--支付下发--结束 response = {}", resp);
		} catch (Exception e) {
			log.error("开闸失败", e);
		}
		return notifyResult;
	}

	private	String httpPost(ParkingLot lot, String uri, String dataJson) {
		HEADER.put("accessToken", lot.getApiKey());
		String resp = postJson(lot.getSn(), lot.getApiUrl() + uri, dataJson, HEADER, lot.getIsTcp());
		if (resp == null)
			throw new MyException(RespMessage.Status.ERROR, Message.COMMON_SERVER_CONNECTION_FAILURE);
		return resp;
	}

	// String getTokenHttp(HttpServletRequest request) {
	// String json = requestToJson(request);
	// if (json == null)
	// return "json获取出错";
	// JSONObject obj = JSONObject.parseObject(json);
	// String userName = obj.getString("userName");
	// String lotSn = obj.getString("lotSn");
	// ParkingLot lot = lotService.findBySn(lotSn);
	// Map<String, Object> param = new HashMap<>();
	// param.put("userName", userName);
	// return get(lot.getApiUrl() + "/ipms/subSystem/generate/token", param);
	// }

	@Override
	public boolean notifyMonthCardChargeOrderSuccess(String order, String lotSn, String carPlate) {
		return false;
	}

	@Override
	public McardQueryResultWrap monthCardQuery(String lotSn, MemberCarPlate carPlate) {
		return null;
	}

	@Override
	public McardQueryResultWrap monthCardChargePreview(String lotSn, String carPlate, Integer month) {
		return null;
	}

	@Override
	public ParkingLotInfoQueryResultWrap parkingLotInfoQuery(String lotSn) {
		return null;
	}

	@Override
	public McardQueryChargeOptionsResultWrap monthCardQueryChargeOptions(String lotSn) {
		return null;
	}

	@Override
	public McardChargeOrderCreateResultWrap monthCardCreateOrder(MemberCarPlate memberCarPlate, CreateMcardChargeOrderDTO dto, Platform platform) {
		return null;
	}

	@Override
	public McardCancelResultWrap monthCardCancel(String lotSn, String carPlate) {
		return null;
	}

	@Override
	public String execute(String api, String lotSn, Map map) {
		return null;
	}

	@Override
	public void afterInNoticeHanler(HttpServletRequest request, HttpServletResponse response, ParkingLot parkingLot) {
		String decryptJson = DaHuaUtils.decodeRequest(request);
		JSONObject json = JSONObject.parseObject(decryptJson);
		String carPlate = json.getString("carNum");
		// JSONObject commParam = json.getJSONObject("commParam");
		// String lotSn = commParam.getString("parkNum");
		// ParkingLot lot = lotService.findBySn(lotSn);
		log.debug("大华--入场后--开始 carPlate = {}, lotName = {}, json = {}", carPlate, parkingLot.getName(), decryptJson);
		String inTime = json.getString("arriveTime");
		// String channelSn = lot.getLotSn();
		ParkingIn in = inService.findByCarPlate(parkingLot.getId(), carPlate);
		// ParkChannel channel = channelService.findBySn(channelSn);
		if (in != null)
			inService.delete(in);
		int actType = json.getIntValue("actType");
		inService.enter(parkingLot, carPlate, DateUtils.parse(inTime, DateUtils.YYYYMMDDHHMMSS), null, parkingLot.getFreeMinutes(), json.getString("bizSn"),
				DaHuaUtils.daHuaActType(actType));
		DaHuaUtils.returnResponse(response, DaHuaResult.successJson());
	}

	@Override
	public void afterOutNoticeHanler(HttpServletRequest request, HttpServletResponse response, ParkingLot parkingLot) {
		String decodeJson = DaHuaUtils.decodeRequest(request);
		JSONObject json = JSONObject.parseObject(decodeJson);
		JSONObject commParam = json.getJSONObject("commParam");
		String channelOutSn = commParam.getString("parkNum");
		// ParkingLot lot = lotService.findBySn(lotSn);
		String carPlate = json.getString("carNum");
		log.debug("大华--出场后--开始 carPlate = {}, lotName = {}, json = {}", carPlate, parkingLot.getName(), decodeJson);
		Date inTime = DateUtils.parse(json.getString("arriveTime"), DateUtils.YYYYMMDDHHMMSS);
		Date outTime = DateUtils.parse(json.getString("leaveTime"), DateUtils.YYYYMMDDHHMMSS);
		ParkingIn in = inService.findByCarPlate(parkingLot.getId(), carPlate);
		float money = json.getFloatValue("payMoney");
		String bizSn = json.getString("bizSn");
		// 判断是否重复出场
		ParkingInOut outOld = outService.findByParkingLotIdAndCarPlateOne(parkingLot.getId(), carPlate);
		int returnType = 0;
		if (outOld != null) {// 有相同的记录
			if (outOld.getMoney() != null) {
				float moneyOld = outOld.getMoney().floatValue();
				// 金额是否相等
				boolean bo = moneyOld == money;
				// 出场时间差
				long diff = outTime.getTime() - outOld.getOutTime().getTime();
				// 存在入场记录 及其订单
				boolean orderExist = in != null && isOrderExist(in.getUniqueId());
				log.debug("大华--出场后--判断条件 diff = {}, bo = {}, orderExist = {}", diff, bo, orderExist);
				if (bo) { // 金额相等
					if (money == 0) { // 金额都未0
						if (diff < 30 * 1000) {
							if (orderExist)
								returnType = 2;// 转移订单 入场时间 删除入场退出
							else
								returnType = 1; // 转移入场时间 删除入场 退出
						}
					} else { // 金额都为 X
						if (diff < 30 * 60 * 1000) { // 都有金额 按照30分钟计算 (30分钟内不会产生两笔金额
							if (orderExist)
								returnType = 2;// 转移订单 入场时间 删除入场退出
							else
								returnType = 1; // 转移入场时间 删除入场 退出
						}
					}
				} else {// 金额不等
					if (diff < 30 * 60 * 1000) {
						if (money > moneyOld) { // 当前基恩大于历史金额
							outOld.setMoney(new BigDecimal(money));
							outService.save(outOld);
						}
						if (orderExist)
							returnType = 2;
						else
							returnType = 1;
					}
				}
			}
		}
		switch (returnType) {
		case 0:
			break;
		case 2:
			List<Order> order = orderService.findByUniqueId(in.getUniqueId());
			order.forEach(o -> {
				o.setUniqueId(outOld.getUniqueId());
				orderService.save(o);
			});
			inService.delete(in);
		case 1:
			if (inTime.getTime() < outOld.getInTime().getTime()) {
				outOld.setInTime(inTime);
				outService.save(outOld);
			}
			DaHuaUtils.returnResponse(response, DaHuaResult.successJson());
			log.debug("大华--出场后--本条为重复数据 carPlate = {}, returnType = {}", carPlate, returnType);
			return;
		default:
		}
		// 清除redis中重复的数据, 放在判断重复数据之后是因为 认为重复数据是延时或异常数据 不进行出场
		ParkChannel channel = channelService.findByChannelSn(parkingLot.getId(), channelOutSn);
		String channelSn = channel.getSn();
		ExitingCar exitingCar = exitingCarService.get(channelSn);
		if (exitingCar != null) {
			// 如果车牌一致 则删除 ; 删除出场时间小于当前出场车辆出场时间的数据, 防止后车数据删除
			if (exitingCar.getCarPlate().equals(carPlate)) {
				exitingCarService.remove(channelSn);
			} else if (DateUtils.parse(exitingCar.getOutTime()).getTime() <= outTime.getTime()) {
				exitingCarService.remove(channelSn);
			}
		}
		// 获取入场纪录 如果没有 则添加
		boolean isInNull = false;
		if (in == null) {
			isInNull = true;
			in = inService.enter(parkingLot, carPlate, inTime, null, parkingLot.getFreeMinutes(), null);
		}
		// 新建出场记录
		ParkingInOut out = new ParkingInOut();
		out.setOutTime(outTime);
		out.setExitId(channel.getId());
		// 大华停车标识设置 为了直观观测数据准确性 可以注释掉
		String inBizSn = in.getLotRecordId();
		bizSn = DaHuaUtils.inOutBizSn(inBizSn, bizSn);
		out.setLotRecordId(bizSn);
		int actType = json.getIntValue("actType");
		out.setCarActType(DaHuaUtils.daHuaActType(actType));
		if (isInNull && outTime.getTime() - inTime.getTime() < 5000 && money == 0)
			out.setCarActType(CarActType.others);
		// 查看出场是否有订单
		Map<String, String> resObj = new HashMap<>();
		resObj.put("payType", "2");
		out.setMoney(new BigDecimal(money));
		out.setPlatform(Platform.cash);
		if (!isInNull) {
			List<Order> orders = orderService.findPaidListByUniqueId(in.getUniqueId());
			if (orders != null && orders.size() > 0) {
				BigDecimal fee = new BigDecimal(0f);
				for (Order order : orders) {
					fee = fee.add(order.getMoney());
					out.setPlatform(order.getPlatform());
				}
				// 如果车厂缴费金额大于我们订单总和, 那么其中包含现金支付 取车厂值
				if (money < fee.floatValue()) {
					out.setMoney(fee);
				}
				resObj.put("payType", "1");
				resObj.put("amountActual", out.getMoney().toString());
				resObj.put("amountReceivable", out.getMoney().toString());
			}
		}
		outService.out(in, out);
		// DaHuaUtils.returnResponse(response, DaHuaResult.successJson(resObj));
		log.debug("大华--出场后--完成 carPlate = {}, uniqueId = {}, money = {}", carPlate, in.getUniqueId(), out.getMoney());
		DaHuaUtils.returnResponse(response, DaHuaResult.successJson(resObj));
	}


	@Override
	public void beforeOutNoticeHandler(HttpServletRequest request, HttpServletResponse response, ParkingLot parkingLot) {
		try {
			String decodeJson = DaHuaUtils.decodeRequest(request);
			JSONObject json = JSONObject.parseObject(decodeJson);
			String carPlate = json.getString("carNum");
			String channelOutSn = json.getString("parkNum");
			// ParkingLot lot = lotService.findBySn(lotSn);
			log.debug("大华--预出场--开始 carPlate = {}, lot = {}, json = {}", carPlate, parkingLot.getName(), decodeJson);
			// 并发问题
			if (!redisService.saveString(carPlate, carPlate, 1)) {
				DaHuaUtils.returnResponse(response, DaHuaResult.CAR_BEFORE_OUT_FAIL_JSON);
				return;
			}
			ParkChannel channel = channelService.findByChannelSn(parkingLot.getId(), channelOutSn);
			String channelSn = channel.getSn();
			Date outTime = DateUtils.parse(json.getString("leaveTime"), DateUtils.YYYYMMDDHHMMSS);
			// 判断出场时间小于当前redis中的数据出场时间 不入 (由网络延迟引起)
			ExitingCar exitingCar = exitingCarService.get(channelSn);
			if (exitingCar != null && DateUtils.parse(exitingCar.getOutTime()).getTime() > outTime.getTime()) {
				DaHuaUtils.returnResponse(response, DaHuaResult.CAR_BEFORE_OUT_FAIL_JSON);
				return;
			}
			// 请求算费
			DaHuaBeforehandFeeRequest req = new DaHuaBeforehandFeeRequest(carPlate, 1);
			if (SettingUtils.get().getDahuaInterfaceMode() != 0)
				req.setParkingLotCode(channel.getChannelSn());
			String resp = httpPost(parkingLot, "/ipms/integration/kingdo/payment/info", JSONObject.toJSONString(req));
			log.debug("大华--预出场--算费 carPlate = {}, json = {}", carPlate, resp);
			JSONObject cost = JSONObject.parseObject(resp);
			boolean isSuccess = cost.getBooleanValue("success");
			if (!isSuccess) {
				DaHuaUtils.returnResponse(response, DaHuaResult.CAR_BEFORE_OUT_FAIL_JSON);
				return;
			}
			isSuccess = cost.containsKey("data");
			if (!isSuccess) {
				// 如果没有费用或者免费车辆 有时会不返回data字段
				DaHuaUtils.returnResponse(response, DaHuaResult.CAR_BEFORE_OUT_FAIL_JSON);
				return;
			}
			JSONObject data = cost.getJSONObject("data");
			float money = data.getFloatValue("receivableMoney");
			log.debug("大华--预出场--算费 carPlate = {}, money = {}", carPlate, money);
			if (money == 0) {
				DaHuaUtils.returnResponse(response, DaHuaResult.CAR_BEFORE_OUT_FAIL_JSON);
				return;
			}
			// 如果额米有入场, 并且算费由费用, 添加入场纪录
			ParkingIn in = inService.findByCarPlate(parkingLot.getId(), carPlate);
			if (in == null) {
				Date inTime = DateUtils.parse(json.getString("arriveTime"), DateUtils.YYYYMMDDHHMMSS);
				int actType = json.getIntValue("actType");
				in = inService.enter(parkingLot, carPlate, inTime, null, parkingLot.getFreeMinutes(), DaHuaUtils.daHuaActType(actType));
			}
			// 存费用到redis
			String inBizSn = in.getLotRecordId();
			String bizSn = inBizSn == null ? "00000:00000" : inBizSn + ":00000";
			int parkMinutes = (int) Math.ceil(json.getIntValue("parkingTimeLength") / 60f);
			exitingCarService.put(channel.getSn(), new ExitingCar(parkingLot.getSn(), channel.getId(), channel.getDeviceId(), carPlate, money, 0f, DateUtils.format(in.getInTime()),
					DateUtils.format(outTime), parkMinutes, null, bizSn, in.getUniqueId()));
			log.debug("大华--预出场--保存Redis carPlate = {}, channelSn = {}", carPlate, channel.getSn());
			ParkFeeCacheBean parkFee = new ParkFeeCacheBean();
			parkFee.setDiscountFee(0f);
			parkFee.setEndTime(outTime);
			parkFee.setExitDevice(channel.getDeviceId());
			parkFee.setExitId(channel.getId());
			parkFee.setParkPluginId(parkingLot.getPluginId());
			parkFee.setUniqueId(in.getUniqueId());
			parkFee.setTotalFee(money);
			parkFeeCacheService.put(parkFee);
		} catch (Exception e) {
			log.error("", e);
		}
		DaHuaUtils.returnResponse(response, DaHuaResult.CAR_BEFORE_OUT_FAIL_JSON);
	}

	private boolean isOrderExist(Long uniqueId) {
		List<Order> order = orderService.findByUniqueId(uniqueId);
		return order != null && order.size() > 0;
	}

}
